﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace MassiveBattle.Controller.Boss
{
	public class BaseBossController : BaseController
	{
		private int level;
		private List<DBoss_New> dataList;

		protected BossStat stat;
		public override BaseStat Stat
		{
			get
			{
				return stat;
			}

			protected set
			{
				stat = value as BossStat;
			}
		}

		public event Action<int, int> EventPhaseChange;
		public event System.Action EventDie;

		protected override bool IsCanAction
		{
			get
			{
				return true;
			}
		}

		public void Init(int id, int level, int hp, ESide side, ELine line, int pos)
		{
			Id = id;
			Name = TextController.GetBossName(Id);
			this.level = level;

			DBoss_New dataCur = null;
			dataList = new List<DBoss_New>();
			foreach (var data in GameData.Inst.GetBossData(Id).OrderBy(data => data.phaseHP))
			{
				DBoss_New dataNew = new DBoss_New();

				dataNew.bossID = data.bossID;
				dataNew.modelID = data.modelID;
				dataNew.phaseHP = data.phaseHP;
				dataNew.phaseSet = data.phaseSet;
				dataNew.enableAtkType = data.enableAtkType;
				dataNew.lv = data.lv;
				dataNew.bossTrait = data.bossTrait;
				dataNew.atkRadius = data.atkRadius;
				dataNew.maxLv = data.maxLv;
				dataNew.weakpoint = data.weakpoint;
				dataNew.atkType = data.atkType;
				dataNew.baseAtk = (short)(data.baseAtk + (level - 1) * data.growAtk);
				dataNew.growAtk = data.growAtk;
				dataNew.assist = data.assist;
				dataNew.baseHP = (short)(data.baseHP + (level - 1) * data.growHP);
				dataNew.growHP = data.growHP;
				dataNew.baseDef = (short)(data.baseDef + (level - 1) * data.growDef);
				dataNew.growDef = data.growDef;
				dataNew.init = data.init;
				dataNew.taunt = data.taunt;
				dataNew.melee = (short)(data.melee + (level - 1) * data.growMelee);
				dataNew.growMelee = data.growMelee;
				dataNew.range = (short)(data.range + (level - 1) * data.growRange);
				dataNew.growRange = data.growRange;
				dataNew.magic = (short)(data.magic + (level - 1) * data.growMagic);
				dataNew.growMagic = data.growMagic;
				dataNew.pulse = (short)(data.pulse + (level - 1) * data.growPulse);
				dataNew.growPulse = data.growPulse;
				dataNew.special = (short)(data.special + (level - 1) * data.growSpecial);
				dataNew.growSpecial = data.growSpecial;
				dataNew.evade = data.evade;
				dataNew.evadeMod = data.evadeMod;
				dataNew.defDamage = data.defDamage;
				dataNew.defDamageMod = data.defDamageMod;
				dataNew.HPMod = data.HPMod;
				dataNew.healRate = data.healRate;
				dataNew.res = data.res;
				dataNew.skillRate = data.skillRate;
				dataNew.skillRateMod = data.skillRateMod;
				dataNew.critRate = data.critRate;
				dataNew.critPower = data.critPower;
				dataNew.attackAssist = data.attackAssist;
				dataNew.defAssist = data.defAssist;
				dataNew.atkInit = data.atkInit;
				dataNew.atkCooltime = data.atkCooltime;
				dataNew.atkCooltimeMod = data.atkCooltimeMod;
				dataNew.moveTime = data.moveTime;
				dataNew.moveTimeMod = data.moveTimeMod;
				dataNew.getFromAtk = data.getFromAtk;
				dataNew.getFromHit = data.getFromHit;
				dataNew.moveSpd = data.moveSpd;
				dataNew.moveSpdMod = data.moveSpdMod;
				dataNew.healPower = data.healPower;
				dataNew.healPowerMod = data.healPowerMod;

				dataList.Add(dataNew);

				if (hp <= Mathf.CeilToInt(dataNew.phaseHP * 0.01f * dataNew.baseHP))
					dataCur = dataNew;
			}

			Stat = BossStat.Create(dataCur, level, Manager.Monitor);

			Init(dataCur.modelID, side, line, pos, EArea.NONE);
			InitSkill();

			HealthPointCur = hp;

			OnPhaseChange(stat.phaseSet, stat.enableAtkType.Get(Manager.Monitor));

			IsAlive = true;
		}

		protected override void InitClass()
		{
			base.InitClass();

			//데미지 폰트 미리 생성//
			if (damageFont == null)
				damageFont = ModelGameObject.AddComponent<DamageFont>();
			for (int k = 1; k <= 3; k++)
			{
				for (int i = 0; i < 8; i++)
				{
					damageFont.ShowDamage(false, false, pointEffectFont, 1, this, null, k);
				}
			}
			for (int k = 4; k <= 6; k++)
			{
				for (int i = 0; i < 3; i++)
				{
					damageFont.ShowDamage(false, true, pointEffectFont, 1, this, null, k);//for skill
				}
			}
			damageFont.DeleteAllDamageFont();
		}

		protected override void InitTake()
		{
			var takePhaseList = dataList.Select(data => data.phaseSet).Distinct();
			var takeIdList = Utility.GetEnumValues(EMassTake.damage01, EMassTake.die01, EMassTake.Max);

			var takeNameSet = new HashSet<string>();

			foreach (var takePhase in takePhaseList)
			{
				foreach (var takeType in takeIdList)
				{
					string takeName = takeType.ToString();

					if (takeType != EMassTake.down01)
						takeName = string.Format("{0:00}_{1}", takePhase, takeName);

					if (takeType == EMassTake.attack01)
					{
						takeNameSet.Add(takeName + "_L");
						takeNameSet.Add(takeName + "_M");
						takeNameSet.Add(takeName + "_R");
					}
					else
						takeNameSet.Add(takeName);
				}
			}

			string takePath = "MassBattle/" + ClassName.ToString().ToLower() + "/";

			foreach (var takeName in takeNameSet)
			{
				var take = Manager.TakePool[takePath + takeName];

				if (take == null)
					continue;

				takeDic.Add(takeName, take);
			}
		}

		protected override void InitFsm()
		{
			FsmMain = new Fsm<EStateMain>();
			FsmMain.AddTransition(EStateMain.idle, EStateMain.enterstage, EStateMain.die);

			var state = FsmMain.AddState(EStateMain.idle);
			state.AddTransition(EStateMain.idle, EStateMain.attack, EStateMain.skill);
			state.EventStart = OnIdleStateStart;
			state.EventTakeEnd = OnIdleStateTakeEnd;

			state = FsmMain.AddState(EStateMain.enterstage);
			state.AddTransition(EStateMain.idle);
			state.EventStart = OnEnterstageStateStart;
			state.EventTakeEnd = OnEnterstateStateTakeEnd;

			state = FsmMain.AddState(EStateMain.die);
			state.EventStart = OnDieStateStart;
			state.EventTakeEnd = OnDieStateTakeEnd;

			state = FsmMain.AddState(EStateMain.attack);
			state.AddTransition(EStateMain.idle);
			state.EventStart = OnAttackStateStart;
			state.EventTakeEnd = OnIdleStateTakeEnd;

			state = FsmMain.AddState(EStateMain.skill);
			state.AddTransition(EStateMain.idle);
			state.EventStart = OnSkillStateStart;
			state.EventTakeEnd = OnIdleStateTakeEnd;
		}

		private void InitSkill()
		{
			var id = stat.bossTrait.Get(Manager.Monitor);

			SkillData = id == 0 ? null : GameData.Inst.GetSkill(id);
		}

		protected override void OnDie()
		{
			if (EventDie != null)
				EventDie();
		}

		private void OnPhaseChange(int phase, int typeFlag)
		{
			if (EventPhaseChange != null)
				EventPhaseChange(phase, typeFlag);
		}

		protected override void CheckPhase(int point)
		{
			var dataCur = dataList.First(data => point <= Mathf.CeilToInt(data.phaseHP * 0.01f * data.baseHP));

			if (stat.phaseHP.Get(Manager.Monitor) == dataCur.phaseHP)
				return;

			ChangePhase(dataCur);
		}

		private void ChangePhase(DBoss_New data)
		{
			var monitor = Manager.Monitor;

			Stat = BossStat.Create(data, level, monitor);

			var phase = stat.phaseSet;

			InitSkill();

			OnPhaseChange(phase, stat.enableAtkType.Get(monitor));
		}

#if UNITY_EDITOR
		public void ChangePhase(int phase)
		{
			var result = dataList.FirstOrDefault(data => data.phaseSet == phase);

			if (result == null)
				return;

			int hp = Mathf.CeilToInt(result.phaseHP * 0.01f * result.baseHP);

			Damage(HealthPointCur - hp, false, null);
		}
#endif

		public override void PlayTake(EMassTake takeType, UnityEngine.Object[] actorList, bool isLoop = false, EArea area = EArea.NONE)
		{
			StopTake();

			string takeName = takeType.ToString();

			if (takeType != EMassTake.down01)
				takeName = Manager.Phase.ToString("00_") + takeName;

			if (takeType == EMassTake.attack01)
			{
				switch (area)
				{
					case EArea.LEFT: takeName += "_L"; break;
					case EArea.MIDDLE: takeName += "_M"; break;
					case EArea.RIGHT: takeName += "_R"; break;
				}
			}
			else if (takeType == EMassTake.idle02)
			{
				if (!takeDic.ContainsKey(takeName))
				{
					PlayTake(EMassTake.idle01, actorList, isLoop);

					return;
				}
			}

			if (takeDic.ContainsKey(takeName))
			{
				takeCur = Eventline.Play(takeDic[takeName], actorList, gameObject, isLoop);
				takeTypeCur = takeType;
			}
			else
				Debug.LogError("PlayTake fail. takeDic not have key  " + takeName + "   " + ModelGameObject.name);
		}

		protected void OnIdleStateStart()
		{
			PlayTake(UnityEngine.Random.value < 0.5f ? EMassTake.idle01 : EMassTake.idle02, new[] { ModelGameObject });
		}

		protected void OnIdleStateTakeEnd()
		{
			FsmMain.Transit(EStateMain.idle);
		}

		protected void OnEnterstageStateStart()
		{
			if (EventlineStaticActor.Camera != null)
				EventlineStaticActor.Camera.transform.parent = null;

			PlayTake(EMassTake.enterstage01, new[] { ModelGameObject });
		}

		protected void OnEnterstateStateTakeEnd()
		{
			ModelTransform.position = Manager.phasePositionRoot == null ? Vector3.zero : Manager.phasePositionRoot.Find(string.Format("{0:00}", Manager.Phase)).position;

			FsmMain.Transit(EStateMain.idle);
		}

		protected void OnDieStateStart()
		{
			if (EventlineStaticActor.Camera != null)
				EventlineStaticActor.Camera.transform.parent = null;

			PlayTake(EMassTake.down01, new[] { ModelGameObject });
		}

		protected void OnDieStateTakeEnd()
		{
			Manager.RemoveMember(this);

			enabled = false;
		}

		protected void OnAttackStateStart()
		{
			var method = GetType().GetMethod("Execute_Attack_Phase" + Manager.Phase, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

			if (method != null)
				method.Invoke(this, null);
		}

		protected void OnSkillStateStart()
		{
			var method = GetType().GetMethod("Execute_Skill_Phase" + Manager.Phase, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

			if (method != null)
				method.Invoke(this, null);
		}
	}
}
